package com.fin.zw.aiqas.service;


import com.alibaba.fastjson.JSONObject;
import com.fin.zw.aiqas.entity.*;
import com.fin.zw.aiqas.enums.Synchronization;
import com.fin.zw.aiqas.enums.Whether;
import com.fin.zw.aiqas.model.component.Drill;
import com.fin.zw.aiqas.model.component.Formula;
import com.fin.zw.aiqas.model.component.SampleInfo;
import com.fin.zw.aiqas.model.request.DrillRequest;
import com.fin.zw.aiqas.model.request.TemplateInfoRequest;
import com.fin.zw.aiqas.model.request.TemplateQueryRequest;
import com.fin.zw.aiqas.model.response.TemplateInfoResponse;
import com.fin.zw.aiqas.model.session.SessionModel;
import com.fin.zw.aiqas.repository.FormulaInfoRepository;
import com.fin.zw.aiqas.repository.ScenesRepository;
import com.fin.zw.aiqas.repository.TemplateInfoRepository;
import com.fin.zw.aiqas.util.*;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.querydsl.core.BooleanBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.util.*;
import java.util.stream.Collectors;

@Service("templateInfoService")
public class TemplateInfoService {

    private final static Logger log = LoggerFactory.getLogger(TemplateInfoService.class);

    @Autowired
    private TemplateInfoRepository templateInfoRepository;

    @Autowired
    private FormulaInfoRepository formulaInfoRepository;

    @Autowired
    private UploadFileService uploadFileService;

    @Autowired
    private ScenesRepository scenesRepository;

    @Autowired
    private Snowflake snowflake;

    @Value("${root.template}")
    private String rootPath;

    @Value("${root.drill}")
    private String drillUrl;


    /**
     * 信息样本的查询
     *
     * @param pageable
     * @return
     */
    public Page<TemplateInfoResponse> queryTemplateInfo(TemplateQueryRequest request, Pageable pageable) {
        QTemplateInfo qTemplateInfo = QTemplateInfo.templateInfo;
        BooleanBuilder builder = new BooleanBuilder();

        if (request.getFormulaId() != null) {
            FormulaInfo formula = formulaInfoRepository.findById(request.getFormulaId()).orElseThrow(() -> new BadRequestException("", "根据Id未查询到质检点信息"));
            builder.and(qTemplateInfo.formulaInfos.contains(formula));
        }

        if (request.getScenesId() != null) {
            builder.and(qTemplateInfo.scenesInfo.id.eq(request.getScenesId()));
        }

        if (request.getContent() != null) {
            builder.and(qTemplateInfo.content.like("%" + request.getContent() + "%"));
        }

        return templateInfoRepository.findAll(builder, pageable).map(m -> {
            TemplateInfoResponse response = new TemplateInfoResponse();
            BeanUtils.copyProperties(m, response);
            response.setScenesId(m.getScenesInfo() != null ? m.getScenesInfo().getId() : null);
            response.setFormulaIds(m.getFormulaInfos()==null? null:m.getFormulaInfos().stream().map(f -> f.getId()).collect(Collectors.toSet()));
            response.setFormulaNames(m.getFormulaInfos()==null?null: String.join(",", m.getFormulaInfos().stream().map(f -> f.getName()).collect(Collectors.toList())));
            return response;
        });
    }

    /**
     * 样本信息的添加
     *
     * @param request
     * @param sessionModel
     */
    public void createInfo(TemplateInfoRequest request, SessionModel sessionModel) {
        TemplateInfo templateInfo = new TemplateInfo();
        BeanUtils.copyProperties(request, templateInfo);

        if (request.getFormulaIds() != null && !request.getFormulaIds().isEmpty()) {
            Iterable<FormulaInfo> iterable = formulaInfoRepository.findAll(QFormulaInfo.formulaInfo.id.in(request.getFormulaIds()));
            Set<FormulaInfo> set = Sets.newHashSet(iterable);
            templateInfo.setFormulaInfos(set);
        }
        ScenesInfo scenesInfo = scenesRepository.findById(request.getScenesId()).orElseThrow(() -> new BadRequestException("", "样本场景异常!!"));
        templateInfo.setScenesInfo(scenesInfo);
        templateInfo.setCreateTime(new Date());
        templateInfo.setOperatorId(sessionModel.getUserId());
        templateInfo.setOperatorRealName(sessionModel.getUserName());
        templateInfoRepository.save(templateInfo);
    }

    /**
     * 样本信息的更新
     *
     * @param request
     * @param sessionModel
     */
    public void updateInfo(TemplateInfoRequest request, SessionModel sessionModel) {
        Optional<TemplateInfo> optional = templateInfoRepository.findById(request.getId());
        if (!optional.isPresent()) {
            throw new BadRequestException("templateInfo", "需要更新的样本信息不存在！");
        }
        TemplateInfo templateInfo = optional.get();
        BeanUtils.copyProperties(request, templateInfo);
        if (request.getFormulaIds() != null && !request.getFormulaIds().isEmpty()) {
            Iterable<FormulaInfo> iterable = formulaInfoRepository.findAll(QFormulaInfo.formulaInfo.id.in(request.getFormulaIds()));
            Set<FormulaInfo> set = Sets.newHashSet(iterable);
            templateInfo.setFormulaInfos(set);
        }
        ScenesInfo scenesInfo = scenesRepository.findById(request.getScenesId()).orElseThrow(() -> new BadRequestException("", "样本场景异常!!"));
        templateInfo.setScenesInfo(scenesInfo);
        templateInfo.setUpdateTime(new Date());
        templateInfo.setUpdateUserId(sessionModel.getUserId());
        templateInfo.setUpdateRealName(sessionModel.getUserName());
        templateInfoRepository.save(templateInfo);
    }

    /**
     * 样本信息的删除
     *
     * @param id
     */
    public void deleteInfo(Long id) {
        templateInfoRepository.deleteById(id);
    }


    /**
     * 样本导出功能
     *
     * @param response
     */
    public void exportTemplate(HttpServletResponse response) {
        List<TemplateInfo> list = templateInfoRepository.findAll();
        Set<FormulaInfo> set = new HashSet<>();//所有的列
        for (TemplateInfo templateInfo : list) {
            set.addAll(templateInfo.getFormulaInfos());
        }
        LinkedHashMap map = new LinkedHashMap();//表头
        List exportData = new ArrayList<Map>();//数据
        Map row = new LinkedHashMap<String, String>();
        for (FormulaInfo formulaInfo : set) {
            map.put(String.valueOf(formulaInfo.getId()), formulaInfo.getName());
            row.put(String.valueOf(formulaInfo.getId()), String.valueOf(formulaInfo.getId()));
        }
        map.put("sample", "样本");
        row.put("sample", "sample");
        exportData.add(row);
        for (TemplateInfo templateInfo : list) {
            Map newRow = new LinkedHashMap<String, String>();
            for (FormulaInfo formulaInfo : set) {
                if (templateInfo.getFormulaInfos().contains(formulaInfo)) {
                    newRow.put(String.valueOf(formulaInfo.getId()), String.valueOf(1));
                } else {
                    newRow.put(String.valueOf(formulaInfo.getId()), String.valueOf(0));
                }
            }
            newRow.put("sample", templateInfo.getContent());
            exportData.add(newRow);
        }
        String fileName = String.valueOf(snowflake.next());
        File file = CSVUtils.createCSVFile(exportData, map, rootPath, fileName);
        FileUtil.download(response, file);
    }

    /**
     * 导入样本配置信息
     *
     * @param fileId
     */
    public void importTemplateInfo(Long fileId, SessionModel sessionModel) throws Exception {
        File file = uploadFileService.findOneFileById(fileId);
        List<Map<String, String>> list = CSVUtils.readCsv(file);
        List<TemplateInfo> templateInfoList = new ArrayList<>();
        for (Map<String, String> map : list) {
            Set<FormulaInfo> set = new HashSet<>();
            String content = new String();
            for (Map.Entry<String, String> entry : map.entrySet()) {
                if (entry.getValue().trim().equals("1")) {
                    Optional<FormulaInfo> optional = formulaInfoRepository.findOne(QFormulaInfo.formulaInfo.name.eq(entry.getKey()));
                    if (optional.isPresent()) {
                        set.add(optional.get());
                    }
                }
                if (entry.getKey().trim().equals("样本")) {
                    content = entry.getValue();
                }
            }
            if (!set.isEmpty()) {
                TemplateInfo templateInfo = new TemplateInfo();
                templateInfo.setFormulaInfos(set);
                templateInfo.setContent(content);
                templateInfo.setCreateTime(new Date());
                templateInfo.setOperatorId(sessionModel.getUserId());
                templateInfo.setOperatorRealName(sessionModel.getUserName());
                templateInfoList.add(templateInfo);
            }
        }
        templateInfoRepository.saveAll(templateInfoList);
    }


    public List<List<Object>> getDrillHead(Long sceId) {
        BooleanBuilder builder = new BooleanBuilder();
        QTemplateInfo qTemplateInfo = QTemplateInfo.templateInfo;
        builder.and(qTemplateInfo.scenesInfo.id.eq(sceId));
        builder.and(qTemplateInfo.scenesInfo.isSynchronize.eq(Synchronization.YES));
        Iterator<TemplateInfo> infoIterator = templateInfoRepository.findAll(builder).iterator();
        Set<FormulaInfo> set = new HashSet<>();//所有的列
        while (infoIterator.hasNext()) {
            TemplateInfo templateInfo = infoIterator.next();
            set.addAll(templateInfo.getFormulaInfos());
        }
        List<FormulaInfo> formulaInfos = new ArrayList<>(set);
        //3.jdk8 升序排序，Comparator提供的静态方法;（降序reversed）根据id进行升序排列
        Collections.sort(formulaInfos, Comparator.comparing(FormulaInfo::getId));
        List<List<Object>> datas = new ArrayList<>();
        List<Object> head = new ArrayList<>();
        List<Object> headId = new ArrayList<>();
        for (FormulaInfo info : formulaInfos) {
            if (info.getIsSynchronize().toString().equals(Whether.YES.toString())) {
                Formula formula = new Formula();
                formula.setFormulaId(info.getId());
                formula.setFormulaName(info.getName());
                head.add(info.getName());
                headId.add(info.getId());
            }
        }
        head.add("样本");
        headId.add("sample");
        datas.add(head);
        datas.add(headId);
        return datas;
    }


    /**
     * 对应的场景进行训练
     *
     * @param sceId
     */
    public void drillBySceId(Long sceId) throws Exception {
        ScenesInfo scenesInfo = new ScenesInfo();
        Iterator<TemplateInfo> infoIterator = templateInfoRepository.findAll(QTemplateInfo.templateInfo.scenesInfo.id.eq(sceId)).iterator();
        List<TemplateInfo> templateInfoList = Lists.newArrayList(infoIterator);
        if (templateInfoList != null && !templateInfoList.isEmpty()) {
            Set<FormulaInfo> set = new HashSet<>();//所有的列
            for (TemplateInfo templateInfo : templateInfoList) {
                set.addAll(templateInfo.getFormulaInfos());
            }
            List<FormulaInfo> formulaInfos = new ArrayList<>(set);
            //3.jdk8 升序排序，Comparator提供的静态方法;（降序reversed）根据id进行升序排列
            Collections.sort(formulaInfos, Comparator.comparing(FormulaInfo::getId));

            List<List<Object>> datas = new ArrayList<>();
            List<Object> head = new ArrayList<>();
            List<Object> headId = new ArrayList<>();
            //获取所有的列（即质检点即排序）
            List<Formula> formulas = new ArrayList<>();
            for (FormulaInfo info : formulaInfos) {
                Formula formula = new Formula();
                formula.setFormulaId(info.getId());
                formula.setFormulaName(info.getName());
                head.add(info.getName());
                headId.add(info.getId());
                formulas.add(formula);
            }
            head.add("样本");
            headId.add("sample");
            datas.add(head);
            datas.add(headId);
            for (TemplateInfo templateInfo : templateInfoList) {
                SampleInfo sampleInfo = new SampleInfo();
                scenesInfo = templateInfo.getScenesInfo();
                sampleInfo.setText(templateInfo.getContent());
                List<Formula> formulaList = new ArrayList<>();
                formulaList.addAll(formulas);
                List<Object> body = new ArrayList<>();
                for (Formula formula : formulaList) {
                    if (exitFormula(templateInfo.getFormulaInfos(), formula.getFormulaId())) {
                        body.add(1);
                    } else {
                        body.add(0);
                    }
                }
                body.add(templateInfo.getContent());
                datas.add(body);
            }
            if (Objects.nonNull(scenesInfo)) {
                Drill drill = new Drill();
                drill.setData(datas);
                drill.setId(scenesInfo.getId().toString());
                drill.setCode(scenesInfo.getScenesCode());
                drill.setName(scenesInfo.getName());
                drill.setCommand("train");
                drill.setStatus("ready");
                drill.setDetailId("");
                String json = JSONObject.toJSONString(drill);
                System.out.println(json);
                log.info("样本场景训练数据:" + json);
                SocketClientUtils.sendText(drillUrl, json);
            } else {
                log.info("在样本中没有找到对应的场景信息");
            }
        } else {
            log.info("该场景下没有找到对应的样本信息");
        }
    }

    /**
     * 判断集合中的质检点是否包含对应的质检点id
     *
     * @param set
     * @param formulaId
     * @return
     */
    public Boolean exitFormula(Set<FormulaInfo> set, Long formulaId) {
        for (FormulaInfo info : set) {
            if (info.getId().equals(formulaId)) {
                return true;
            }
        }
        return false;
    }


    /**
     * 训练完成时更新对应的产经和质检点id
     *
     * @param request
     */
    @Transactional
    public void updateSceState(DrillRequest request) {
        log.info("接受的参数：" + request);
        Long sceId = request.getId();
        if (request.getStatus().equals("finish")) {
            ScenesInfo scenesInfo = new ScenesInfo();
            Iterator<TemplateInfo> infoIterator = templateInfoRepository.findAll(QTemplateInfo.templateInfo.scenesInfo.id.eq(sceId)).iterator();
            List<TemplateInfo> templateInfoList = Lists.newArrayList(infoIterator);
            Set<FormulaInfo> set = new HashSet<>();//所有的列
            for (TemplateInfo templateInfo : templateInfoList) {
                if (Objects.nonNull(scenesInfo)) {
                    BeanUtils.copyProperties(templateInfo.getScenesInfo(), scenesInfo);
                }
                set.addAll(templateInfo.getFormulaInfos());
            }

            List<FormulaInfo> formulaInfoList = new ArrayList<>();
            for (FormulaInfo info : set) {
                info.setIsSynchronize(Synchronization.YES);
                formulaInfoList.add(info);
            }

            if (Objects.nonNull(scenesInfo)) {
                scenesInfo.setIsSynchronize(Synchronization.YES);
                scenesRepository.save(scenesInfo);
            }
            formulaInfoRepository.saveAll(formulaInfoList);
        } else if (request.getStatus().equals("excuting")) {
            log.info("场景id：" + sceId + "正在训练中！");
            ScenesInfo scenesInfo = scenesRepository.findById(request.getId()).orElseThrow(() -> new BadRequestException("", "没有找到对应的场景信息"));
            scenesInfo.setIsSynchronize(Synchronization.EXECUTE);
            scenesRepository.save(scenesInfo);
        } else {
            log.info("场景id：" + sceId + "训练失败！");
        }
    }
}
